package gruppe5.dame.server.main;

import java.util.ArrayList;

import gruppe5.dame.model.DameModel;
import gruppe5.dame.model.DameModelChangedEvent;
import gruppe5.dame.model.DameModelListener;
import gruppe5.dame.model.Pawn;
import gruppe5.dame.model.types.DameEventType;
import gruppe5.dame.model.types.DameGameType;
import gruppe5.dame.model.types.PawnColor;
import gruppe5.dame.server.rfc.cRFC;
import gruppe5.dame.server.rfc.iRFCListener;
import gruppe5.dame.server.rfc.objects.cChatMessage;
import gruppe5.dame.server.rfc.objects.cError;
import gruppe5.dame.server.rfc.objects.cGameCreateRequest;
import gruppe5.dame.server.rfc.objects.cGameJoinRequest;
import gruppe5.dame.server.rfc.objects.cGameMove;
import gruppe5.dame.server.rfc.objects.cLobby;
import gruppe5.dame.server.rfc.objects.cLoginRequest;
import gruppe5.dame.server.rfc.objects.cLoginResponse;
import gruppe5.dame.server.rfc.objects.cPackageHeader;
import gruppe5.dame.server.rfc.objects.cPackageHeader.PackageType;
import gruppe5.dame.server.rfc.objects.cSpielfeld;

public class cMain implements iRFCListener, iGameChangedListener {
	
	// Main function
	public static void main(String [ ] args) {
		cMain oMain = new cMain();
	}
	
	cRFC oRFC;
	
	ArrayList<cClient> oClients;
	ArrayList<cGame> oGames;
	
	int iServerPort;
	
	public cMain() {
		do_Initialize();
		do_Preset();
		do_Startup();
	}
	
	private void do_Initialize() {
		oRFC = new cRFC();
		oClients = new ArrayList<cClient>();
		oGames = new ArrayList<cGame>();
	}
	
	private void do_Preset() {
		oRFC.AddListener(this);
		iServerPort = 54322;
	}
	
	private void do_Startup() {
		if (oRFC.Server_StartListening(iServerPort)) {
			System.out.println("Server listening on port " + iServerPort);
		} else { 
			System.out.println("Port " + iServerPort + " is blocked, exiting...");
			System.exit(-1);
		}
	}
	
	
	
	//////////////////////////////////////////////////
	////////// PRIVATE METHODS
	//////////////////////////////////////////////////
	
	private cClient get_ClientByID(int iID) {
		System.out.println("sEARCHING FOR : " + iID);
		System.out.println("Clients : " + oClients.size());
		for (int i = 0; i < oClients.size(); i++) {
			
			System.out.println(iID + " == " + oClients.get(i).iID);
			if (iID == oClients.get(i).iID) {
				return oClients.get(i);
			}
		}
		return null;
	}
	
	private boolean remove_ClientByID(int iID) {
		for (int i = 0; i < oClients.size(); i++) {
			if (iID == oClients.get(i).iID) {
				oClients.remove(i);
				return true;
			}
		}
		return false;
	}
	

	private cLobby create_LobbyPackage() {
		int iTemp = 0;
		String[] sUsers;
		String[][] sGames;
		
		for (int i = 0; i < oClients.size(); i++) {
			if (!oClients.get(i).bIsIngame) {
				iTemp++;
			}
		}
		
		// users
		if (iTemp == 0) {
			sUsers = null;
		} else {
			sUsers = new String[iTemp];
			iTemp = 0;
			for (int i = 0; i < oClients.size(); i++) {
				if (!oClients.get(i).bIsIngame) {
					sUsers[iTemp] = oClients.get(i).sUsername;
					iTemp++;
				}
			}
		}

		// games
		if (oGames.size() == 0) {
			sGames = null;
		} else {
			sGames = new String[oGames.size()][3];
			for (int i = 0; i < oGames.size(); i++) {

				sGames[i][0] = oGames.get(i).sGamename;
				sGames[i][1] = get_ClientByID(oGames.get(i).iPlayerOneID).sUsername;
				sGames[i][2] = "17";
			}
		}
		
		return new cLobby(cPackageHeader.PackageType.Lobby, sUsers, sGames);
	}
	
	
	private void broadcast_Lobby() {
		cLobby oLobby = create_LobbyPackage();
		
		//System.out.println("Lobby length: " + oLobby.usernames.length);
		//if (oLobby.gamenames == null) System.out.println("Games = null");
		
		for (int i = 0; i < oClients.size(); i++) {
			if (oClients.get(i).bIsLoggedIn) {
				
				oRFC.Server_SendPackage(oClients.get(i).iID, oLobby);
			}
		}
	}
	
	
	//////////////////////////////////////////////////
	////////// RFC_LISTENER IMPLEMENTATION
	//////////////////////////////////////////////////
	
	@Override
	public void OnServer_Listening(int iID) {
		// TODO Auto-generated method stub
		System.out.println("Event LISTENING fired: " + iID);
	}

	@Override
	public void OnServer_NewClient(int iServerSocketID, int iNewClientSocketID) {
		// TODO Auto-generated method stub
		System.out.println("Event NEW CLIENT fired: " + iServerSocketID + " | " + iNewClientSocketID);
		cClient oClient = new cClient(iNewClientSocketID);
		oClients.add(oClient);
	}

	@Override
	public void OnAll_ConnectionEstablished(int iID) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void OnAll_ObjectReceived(int iID, PackageType eType,
			cPackageHeader oPackage) {
		// TODO Auto-generated method stub
		System.out.println("Event OBJECT RECEIVED fired: " + iID);
	}

	@Override
	public void OnAll_LoginRequestReceived(int iID, cLoginRequest oObject) {
		System.out.println("Event LOGIN REQUEST fired: " + iID);
		// TODO Auto-generated method stub
		cClient oClient = get_ClientByID(iID);
		
		if (oClient != null) {
			if (oClient.bIsLoggedIn == false) {
				// todo: check client version
				// check username length
				if ((oObject.username.length() > 4) && (oObject.username.length() < 21)) {
					// update client info
					oClient.sUsername = oObject.username;
					oClient.bIsLoggedIn = true;
					if (oRFC.Server_SendPackage(iID, new cLoginResponse(cPackageHeader.PackageType.LoginResponse, oClient.sUsername, true))) {
						System.out.println("User logged in: " + oObject.username);
						broadcast_Lobby();
						return;
					}
				}
				System.out.println("User login failed: " + oObject.username);
				oRFC.Server_SendPackage(iID, new cLoginResponse(cPackageHeader.PackageType.LoginResponse, oClient.sUsername, false));
				return;
			}
		}
		System.out.println("User login invalid, removed");
		// disconnect client
		remove_ClientByID(iID);
		oRFC.Server_DisconnectClient(iID);
	}

	@Override
	public void OnAll_LoginResponseReceived(int iID, cLoginResponse oObject) {
		// TODO Auto-generated method stub
		remove_ClientByID(iID);
		oRFC.Server_DisconnectClient(iID);
	}

	@Override
	public void OnAll_LobbyReceived(int iID, cLobby oObject) {
		// TODO Auto-generated method stub
		remove_ClientByID(iID);
		oRFC.Server_DisconnectClient(iID);
	}

	@Override
	public void OnAll_ChatMessageReceived(int iID, cChatMessage oObject) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void OnAll_GameCreateRequestReceived(int iID,
			cGameCreateRequest oObject) {
		// TODO Auto-generated method stub
		System.out.println("Event GAME CREATE fired: " + iID);
		DameGameType eGameType = DameGameType.SMALL;
		String username = get_ClientByID(iID).sUsername;
		oGames.add(new cGame(username +"'s Spiel", iID, eGameType));
		broadcast_Lobby();
	}

	@Override
	public void OnAll_GameJoinRequestReceived(int iID, cGameJoinRequest oObject) {
		// TODO Auto-generated method stub
		System.out.println("Event GAME JOIN fired: " + iID);
		for(cGame game : oGames)
		{
			System.out.println(game.sGamename);
			System.out.println(oObject.gamename);
			if(game.sGamename.equals(oObject.gamename))
			{
				game.iPlayerTwoID = iID;
				game.model = new DameModel(game.eGameType);
				game.model.addDameModelListener(game);
				game.listener = this;
				Pawn[] pawns = game.model.getAllPawns();
				
				oRFC.Server_SendPackage(game.iPlayerOneID, new cSpielfeld(cPackageHeader.PackageType.Spielfeld, DameEventType.GAME_STARTS, game.ePlayerOneColor, game.eGameType, pawns));
				oRFC.Server_SendPackage(game.iPlayerTwoID, new cSpielfeld(cPackageHeader.PackageType.Spielfeld, DameEventType.GAME_STARTS, game.ePlayerTwoColor, game.eGameType, pawns));
				
				return;
			}
		}
	}

	@Override
	public void OnAll_SpielfeldReceived(int iID, cSpielfeld oObject) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void OnAll_GameMoveReceived(int iID, cGameMove oObject) {
		// TODO Auto-generated method stub
		System.out.println("Event GAME MOVE RECEIVED fired: " + iID);
		System.out.println("PawnID " + oObject.pawnId +" Direction: " +oObject.direction);
		for(cGame game : oGames)
		{
			if(game.iPlayerOneID == iID)
			{
				if(game.model.getCurrentPlayer() == game.ePlayerOneColor)
				{
					game.model.movePawn(oObject.pawnId, oObject.direction);
					return;
				}
			}
			if (game.iPlayerTwoID == iID)
			{
				if(game.model.getCurrentPlayer() == game.ePlayerTwoColor)
				{
					game.model.movePawn(oObject.pawnId, oObject.direction);
					return;
				}
			}
			
		}
	}

	@Override
	public void OnAll_ErrorReceived(int iID, cError oObject) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void OnAll_Disconnected(int iID) {
		// TODO Auto-generated method stub
		
	}
	
	
	//////////////////////////////////////////////////
	////////// GAME_LISTENER IMPLEMENTATION
	//////////////////////////////////////////////////

	@Override
	public void gameChanged(DameModelChangedEvent event, String sGamename) {
		// TODO Auto-generated method stub
		cGame tmpGame = null;
		for(cGame game : oGames)
		{
			if(game.sGamename.equals(sGamename))
			{
				Pawn[] pawns = game.model.getAllPawns();
				System.out.println("Event SPIELFELD SENT: ");
				oRFC.Server_SendPackage(game.iPlayerOneID, new cSpielfeld(cPackageHeader.PackageType.Spielfeld, event.type, game.ePlayerOneColor, game.eGameType, pawns));
				oRFC.Server_SendPackage(game.iPlayerTwoID, new cSpielfeld(cPackageHeader.PackageType.Spielfeld, event.type, game.ePlayerTwoColor, game.eGameType, pawns));
				
				if(event.isGameOver())
				{
					tmpGame = game;
					break;
				}
			}
		}
		
		if(event.isGameOver())
		{
			oGames.remove(tmpGame);
		}
	}
	
}
